package core.util;

public class Sequence {

	private long currentValue;

	private long startValue = 1;

	private long increment = 1;

	private long minValue = 1;

	private long maxValue = Long.MAX_VALUE;

	private boolean isCycle = true;

	private boolean isInitialized = false;

	public Sequence() {
	}

	public Sequence(long minValue, long maxValue, long increment, long startValue, boolean isCycle) {
		if (minValue >= maxValue) {
			throw new IllegalArgumentException("The minValue must be less than maxValue.");

		} else if (increment == 0L) {
			throw new IllegalArgumentException("The increment must be a non-zero long.");
			
		} else if (startValue > maxValue) {
			throw new IllegalArgumentException("The startValue cannot be more than maxValue.");

		} else if (startValue < minValue) {
			throw new IllegalArgumentException("The startValue cannot be less than minValue.");
		
		}

		this.minValue = minValue;
		this.maxValue = maxValue;
		this.increment = increment;
		this.startValue = startValue;
		this.isCycle = isCycle;
	}

	public synchronized long nextValue() {
		if (isInitialized) {
			long nextVal = currentValue + increment;
			if (nextVal < minValue) {
				if (isCycle) {
					currentValue = maxValue;

				} else {
					throw new RuntimeException("The nextValue goes below minValue.");
				}
			} else if (nextVal > maxValue) {
				if (isCycle) {
					currentValue = minValue;

				} else {
					throw new RuntimeException("The nextValue exceeds maxValue.");
				}
			} else {
				currentValue = nextVal;
			}
		} else {
			initialize();
		}

		return currentValue;
	}
	
	public synchronized long currentValue() {
		if (isInitialized) {
			return currentValue;
			
		} else {
			throw new RuntimeException("The sequence isn't initialized.");
		}
	}
	
	public synchronized Sequence reset() {
		return reset(false);
	}

	public synchronized Sequence reset(boolean isForce) {
		if (isForce || needReset()) {
			isInitialized = false;
		}

		return this;
	}

	public synchronized boolean isInitialized() {
		return isInitialized;
	}
	
	protected boolean needReset() {
		return isInitialized;
	}
	
	protected void initialize() {
		currentValue = startValue;
		isInitialized = true;
	}
}
